﻿using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Security;
using PM.Entities;

namespace PM.BusinessLayer
{
    public class UserController
    {
        /************************************************************
         * Метод повертає список зареєстрованих користувачів
         * Параметр startIndex - вказує на індекс запису з якого починати
         * вибірку, maxRowCount - кількість записів, що потрібно вибрати
         * **********************************************************/
        public List<User> SelectAll(int startIndex, int maxRowCount)
        {
            /************************************************************
            * using гарантує закриття доступу до БД після виходу з нього
            * **********************************************************/
            using (DataContext dc = Infrastructure.DataContext)
            {
                return dc.GetTable<User>().Skip(startIndex).Take(maxRowCount).ToList();
            }
        }

        /************************************************************
         * допоміжний метод для забезпечення функціональності Pager'a
         * **********************************************************/
        public int GetCount()
        {
            using (DataContext dc = Infrastructure.DataContext)
            {
                return dc.GetTable<User>().Count();
            }
        }

        /************************************************************
         * Метод оновлення даних користувача. Ідентифікатор запису не змінюється
         * **********************************************************/
        public void Update(User entity)
        {
            using (DataContext dc = Infrastructure.DataContext)
            {
                //Витягуємо з БД запис, який потрібно змінити
                User original = GetUser(entity.UserID);

                //якщо поле пароль було пустим...
                if (entity.Password == null)
                {
                    //... то пароль залишиться тим, що й був
                    entity.Password = original.Password;
                }
                //оновляємо дані
                dc.GetTable<User>().Attach(entity, original);
                //обов'язково підтверджуємо всі дії з даними БД методом SubmitChanges()
                dc.SubmitChanges();
            }
        }

        /************************************************************
         * Метод додавання нового користувача до БД.
         * **********************************************************/
        public void Insert(User user)
        {
            using (DataContext dc = Infrastructure.DataContext)
            {
                dc.GetTable<User>().InsertOnSubmit(user);
                dc.SubmitChanges();
            }
        }

        /************************************************************
         * Метод знаходить юзера в БД за його ідентифікатором, 
         * та повертає дані користувача
         * **********************************************************/
        public User GetUser(int id)
        {
            using (DataContext dc = Infrastructure.DataContext)
            {
                return dc.GetTable<User>().SingleOrDefault(r => r.UserID == id);
            }
        }

        /************************************************************
         * Метод видаляє користувача з БД
         * **********************************************************/
        public void Delete(User user)
        {
            using (DataContext dc = Infrastructure.DataContext)
            {
                //Обов'язково для видалення існуючий запис БД повинен бути вибраний відмінним
                //екземпляром DataContext від того, що маємо при реалізації видалення
                dc.GetTable<User>().DeleteOnSubmit(dc.GetTable<User>().SingleOrDefault(r => r.UserID == user.UserID));
                dc.SubmitChanges();
            }
        }

        /************************************************************
         * Метод вибирає з БД і повертає список усіх ролей
         * **********************************************************/
        public List<Role> GetAllRoles()
        {
            using (DataContext dc = Infrastructure.DataContext)
            {
                return dc.GetTable<Role>().ToList();
            }
        }

        /*********************************************************************
         * Метод повертає список доступних ролей для конкретного користувача
         * *******************************************************************/
        public List<UserInRole> GetUserInRoles(int userId)
        {
            using (DataContext dc = Infrastructure.DataContext)
            {
                //DataLoadOptions дозволяє разом із сутністю підтягнути
                //задані сутності з якими перша має зовнішній зв'язок
                DataLoadOptions options = new DataLoadOptions();
                options.LoadWith<UserInRole>(r => r.Role);
                dc.LoadOptions = options;
                return dc.GetTable<UserInRole>().Where(ur => ur.UserID == userId).ToList();
            }
        }

        /*********************************************************************
         * Метод перевіряє належність користувача до всіх ролей
         * *******************************************************************/
        public bool IsAllUserInRoles(int userId)
        {
            using (DataContext dc = Infrastructure.DataContext)
            {
                int roleCount = dc.GetTable<Role>().ToList().Count; //запам'ятовуємо кількість ролей
                //Порівнюємо з кількістю тих, які належать користувачу
                return dc.GetTable<UserInRole>().Where(ur => ur.UserID == userId).ToList().Count < roleCount;
            }
        }

        /***********************************************************
         * Метод видаляє користувача з ролі
         * *********************************************************/
        public void DisableRole(UserInRole userInRole)
        {
            using (DataContext dc = Infrastructure.DataContext)
            {
                UserInRole entity = dc.GetTable<UserInRole>().SingleOrDefault(r => r.UIRID == userInRole.UIRID);
                dc.GetTable<UserInRole>().DeleteOnSubmit(entity);
                dc.SubmitChanges();
            }
        }

        /***********************************************************
         * Метод повертає ролі, яким користувач не належить
         * *********************************************************/
        public List<Role> GetMissingRoles(int userId)
        {
            using (DataContext dc = Infrastructure.DataContext)
            {
                //отримуємо ролі, яким належить користувач
                var userRoles = dc.GetTable<UserInRole>().Where(r => r.UserID == userId).Select(r => r.Role);
                //вибираємо всі ролі і пропускаємо ті, що в списку userRoles
                return dc.GetTable<Role>().Except(userRoles).ToList();
            }
        }

        /***********************************************************
         * Метод додає користувачу роль
         * *********************************************************/
        public void AddToRole(int userId, int roleId)
        {
            using (DataContext dc = Infrastructure.DataContext)
            {
                //створюємо новий екземпляр класу UserInRole
                UserInRole uir = new UserInRole()
                     {
                         RoleID = roleId,
                         UserID = userId
                     };
                //і вставляємо в БД
                dc.GetTable<UserInRole>().InsertOnSubmit(uir);
                dc.SubmitChanges();
            }
        }

        /*************************************************************************
         * Метод перевіряє наявність користувача з вхідними параметрами
         * логін та пароль. Якщо користувач з такими параметрами існує,
         * то повертається його запис з БД, якщо ні - то генерується виключення
         * ***********************************************************************/
        public User Login(string login, string password)
        {
            using (DataContext dc = Infrastructure.DataContext)
            {
                //Вибираємо з БД користувача з вхідними параметрами
                User user = dc.GetTable<User>()
                    .SingleOrDefault(r => r.Login.ToLower() == login.ToLower() && r.Password == password);
                
                if (user == null) //якщо не знайшли
                {
                    throw new SecurityException(); //генеруємо виключення
                }
                return user; //в позитивному випадку повертаємо екземпляр користувача
            }
        }
    }

}
